/**
 * @brief 工厂模式实现
 * @version 1.0
 * @author 宋炜
 * @date 2023-4-25~2024-3-10
 *  新增了支持shared_ptr的功能
 *  新增了支持回调函数的方式
 */

#pragma once
#include <type_traits>
#include <functional>
#include <iostream>

#include <type_traits>
#include <functional>
#include <iostream>
#include <memory>

namespace wheels
{namespace dm{     
		template< typename T >
		struct Factory
		{
		public:
			using realType = typename std::remove_pointer< typename std::decay< T >::type >::type;
			static_assert( std::is_class<realType>::value  , "this function must be used with class" );
			
			template< typename ...Args >
			static realType * factoryCallback( std::function< void ( realType * ) > fun , Args&&... args ){
				realType * ret = nullptr;
				
				try{
					ret = new realType( std::forward<Args>(args)... );
					fun( ret );
				}catch( std::bad_alloc& e ){
					ret = nullptr;
				}catch( ... ){
					delete ret;
					ret = nullptr;
				}
				return ret;
			}
			
			template< typename ...Args >
			static std::shared_ptr< realType > 
			factoryShared( Args&&... args ){
				std::shared_ptr< realType > ret;
				try{
					ret = std::make_shared< realType >( std::forward<Args>(args)... );
				}catch( std::bad_alloc& e ){
					return {};
				}catch( ... ){
					return {};
				}
				return ret;
			}
			
			/**
			*/
			template< typename ...Args >
			static std::shared_ptr< realType > 
			factorySharedCallback( std::function< void ( std::shared_ptr<realType> ) > fun , Args&&... args ){
				std::shared_ptr<realType> ret;
				try{
					ret  = std::make_shared< realType >( std::forward<Args>(args)... );
					fun( ret );
				}catch( std::bad_alloc& e ){
					ret = nullptr;
				}catch( ... ){
					ret.reset();
				}
				
				return ret;
			}

			template< typename ...Args >
			static realType * factory( Args&&... args ){
				
				realType * ret = new realType( std::forward<Args>(args)... );

				return ret;
			}
			
			template< typename ...Args >
			static std::shared_ptr< realType > 
			factorySharedRaw( Args&&... args ){
				
				auto ret = std::make_shared< realType >( std::forward<Args>(args)... );

				return ret;
			}
		};

		
		/**
		 * @brief 模板工厂函数
		 * @tparam type 类名称		 
		 * @tparam Args... ， 调用构造函数的时候构造函数的参数类型表
		 * @param args [ I ], 构造函数的参数表
		 * @param errfun[ I ],发生错误的回调函数
		 */
		template< typename type , typename ...Args ,
			  typename midType = typename std::decay< type >::type,
			  typename realType = typename std::conditional<
				  std::is_pointer< midType >::value ,
				  typename std::remove_pointer<midType>::type,
				  midType
				  >::type
			  >
		realType * factory( Args&&... args ){
			static_assert( std::is_class<realType>::value  , "this function must be used with class" );
			
			realType * ret = nullptr;
			try{
				realType * ret = new realType( std::forward< Args >(args)... );
			}catch( std::bad_alloc& e ){
				std::cout << e.what() << std::endl;
			}
			
			return ret;
		}
		
		template< typename type , typename ...Args ,
			  typename midType = typename std::decay< type >::type,
			  typename realType = typename std::conditional<
				  std::is_pointer< midType >::value ,
				  typename std::remove_pointer<midType>::type,
				  midType
				  >::type
			  >
		std::shared_ptr< realType >
		factoryShared( Args&&... args ){
			static_assert( std::is_class<realType>::value  , "this function must be used with class" );
			std::shared_ptr< realType > ret;
			try{
				ret = std::make_shared< realType >( std::forward< Args >(args)... );
			}catch( std::bad_alloc& e ){
				std::cout << e.what() << std::endl;
			}catch( ... ){
				ret.reset();
			}
			
			return ret;
		}
		
		template< typename type , typename ...Args ,
			  typename midType = typename std::decay< type >::type,
			  typename realType = typename std::conditional<
				  std::is_pointer< midType >::value ,
				  typename std::remove_pointer<midType>::type,
				  midType
				  >::type
			  >
		std::shared_ptr< realType >
		factorySharedCallback( std::function< void ( std::shared_ptr<realType> ) > fun , Args&&... args ){
			static_assert( std::is_class<realType>::value  , "this function must be used with class" );
			std::shared_ptr< realType > ret;
			try{
				ret = std::make_shared< realType >( std::forward< Args >(args)... );
			}catch( std::bad_alloc& e ){
				std::cout << e.what() << std::endl;
			}catch( ... ){
				ret.reset();
			}
			
			return ret;
		}
	}	       
}